package awk.script.javassist.method04.stub;

import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

import java.io.IOException;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * @author yujianlong
 * @create 2021-03-28 22:33
 **/
public class StubTest {


	/***
	 * 输出监控信息
	 *
	 * @author yujianlong
	 * @date 2021/3/29
	 * @return void
	 */
	public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException {
		ClassPool classPool = ClassPool.getDefault();

		CtClass ctClass = classPool.get("com.yu.test.javassist.method04.stub.StubApi");
		//获取类名
		String clazzName = ctClass.getName();

		//获取方法
		CtMethod makeStar = ctClass.getDeclaredMethod("makeStar", new CtClass[]{classPool.get(String.class.getName()),classPool.get(String.class.getName()),classPool.get(Boolean.class.getName())});
		//获取方法名
		String methodName = makeStar.getName();
		//获取方法信息
		// 方法信息
		MethodInfo methodInfo = makeStar.getMethodInfo();
		// 方法类型；静态方法判断
		boolean isStatic = Modifier.isStatic(makeStar.getModifiers());
		//方式2	//(methodInfo.getAccessFlags() & AccessFlag.STATIC) != 0;
		// 方法：入参信息{名称和类型}
		CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
		LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
		CtClass[] parameterTypes = makeStar.getParameterTypes();

		// 方法；出参信息
		CtClass returnType = makeStar.getReturnType();
		String returnTypeName = returnType.getName();

		//获取参数个数
		int parameterSize = isStatic ? attr.tableLength() : attr.tableLength() - 1;

		System.out.println("类名：" + clazzName);
		System.out.println("方法：" + methodName);
		System.out.println("类型：" + (isStatic ? "静态方法" : "非静态方法"));
		System.out.println("描述：" + methodInfo.getDescriptor());
		System.out.println("入参个数：" + parameterSize);

		//普通方法还是静态方法的偏移量 static 往前算一位
		int offset=isStatic ? 0 : 1;

		String paraNames = IntStream.rangeClosed(0, parameterSize - 1).boxed()
				.map(i -> attr.variableName(i + offset))
				.collect(Collectors.joining(","));

		System.out.println("入参[名称]："+paraNames);

		String paraTypes = IntStream.rangeClosed(0, parameterSize - 1).boxed()
				.map(i ->parameterTypes[i].getName())
				.collect(Collectors.joining(","));

		System.out.println("入参[类型]："+paraTypes);


		System.out.println("出参[类型]：" + returnTypeName);


		//构建对象
		MethodDesc methodDesc = new MethodDesc();
		methodDesc.setClazzName(clazzName);
		methodDesc.setStaticFlag(isStatic);
		methodDesc.setMethodName(methodName);
		methodDesc.setParameterNames(paraNames);
		methodDesc.setParameterTypes(paraTypes);
		methodDesc.setReturnType(returnTypeName);


		//参数 $1 $2 ....
		String paraValues = IntStream.rangeClosed(1, parameterSize).boxed()
				.map(i -> "$" + i)
				.collect(Collectors.joining(","));

		String metaDataId = StubMonitor.getMetaDataId(methodDesc);

		// 定义属性
		makeStar.addLocalVariable("startNanos", CtClass.longType);

		// 方法前加强
		makeStar.insertBefore("{ startNanos = System.nanoTime(); }");

		// 定义属性object[]数组
		makeStar.addLocalVariable("paraValues", classPool.get(Object[].class.getName()));

		// 方法前加强
		makeStar.insertBefore("{ paraValues = new Object[]{" + paraValues + "}; }");

		// 方法后加强 $_代表返回值
		makeStar.insertAfter("{com.yu.test.javassist.method04.stub.StubMonitor.printMonitorSuccess(\"" + metaDataId + "\",8L,paraValues,$_);}", false); // 如果返回类型非对象类型，$_ 需要进行类型转换


		// 方法；添加TryCatch
		makeStar.addCatch("{com.yu.test.javassist.method04.stub.StubMonitor.printMonitorFail(\"" + metaDataId +  "\", $e); throw $e; }", ClassPool.getDefault().get("java.lang.Exception"));   // 添加异常捕获

		// 输出类的内容
		ctClass.writeFile("/Users/yujianlong/Movies/test3/StubTest00.class");
		Class aClass1 = ctClass.toClass();
		ctClass.detach();

		new StubApi().makeStar("ss", "sds", true);
	}

	/***
	 * 获取入参出参
	 *
	 * @author yujianlong
	 * @date 2021/3/28
	 * @return void
	 */
	public static void mainbak(String[] args) throws NotFoundException {
		ClassPool classPool = ClassPool.getDefault();
		CtClass ctClass = classPool.get("com.yu.test.javassist.method04.stub.StubApi");
		//获取类名
		String clazzName = ctClass.getName();

		//获取方法
		CtMethod makeStar = ctClass.getDeclaredMethod("makeStar", new CtClass[]{classPool.get(String.class.getName()),classPool.get(String.class.getName()),classPool.get(Boolean.class.getName())});
		//获取方法名
		String methodName = makeStar.getName();
		//获取方法信息
		// 方法信息
		MethodInfo methodInfo = makeStar.getMethodInfo();
		// 方法类型；静态方法判断
		boolean isStatic = Modifier.isStatic(makeStar.getModifiers());
			//方式2	//(methodInfo.getAccessFlags() & AccessFlag.STATIC) != 0;
		// 方法：入参信息{名称和类型}
		CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
		LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
		CtClass[] parameterTypes = makeStar.getParameterTypes();

		// 方法；出参信息
		CtClass returnType = makeStar.getReturnType();
		String returnTypeName = returnType.getName();

		//获取参数个数
		int parameterSize = isStatic ? attr.tableLength() : attr.tableLength() - 1;

		System.out.println("类名：" + clazzName);
		System.out.println("方法：" + methodName);
		System.out.println("类型：" + (isStatic ? "静态方法" : "非静态方法"));
		System.out.println("描述：" + methodInfo.getDescriptor());
		System.out.println("入参个数：" + parameterSize);

		//普通方法还是静态方法的偏移量 static 往前算一位
		int offset=isStatic ? 0 : 1;

		String paraNames = IntStream.rangeClosed(0, parameterSize - 1).boxed()
				.map(i -> attr.variableName(i + offset))
				.collect(Collectors.joining(","));

		System.out.println("入参[名称]："+paraNames);

		String paraTypes = IntStream.rangeClosed(0, parameterSize - 1).boxed()
				.map(i ->parameterTypes[i].getName())
				.collect(Collectors.joining(","));

		System.out.println("入参[类型]："+paraTypes);


		System.out.println("出参[类型]：" + returnTypeName);

	}

}
